package org.playground.booking.service.impl;

import org.apache.log4j.Logger;
import org.playground.booking.dao.IBookingDao;
import org.playground.booking.dao.IPlaygroundDao;
import org.playground.booking.dao.IPlaygroundTypeDao;
import org.playground.booking.dto.*;
import org.playground.booking.entity.*;
import org.playground.booking.enums.BookingStatusEnum;
import org.playground.booking.service.IPlaygroundService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import sun.util.resources.cldr.es.CalendarData_es_HN;

import java.awt.print.Book;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by root on 17-10-29.
 */
@Service
public class PlaygroundServiceImpl implements IPlaygroundService {
    private static final Logger LOGGER = Logger.getLogger(PlaygroundServiceImpl.class);

    @Autowired
    private IPlaygroundDao playgroundDao;

    @Autowired
    private IPlaygroundTypeDao playgroundTypeDao;

    @Autowired
    private IBookingDao bookingDao;


    public void createPlayground(Playground toCreate) {
        playgroundDao.save(toCreate);
    }

    public void updatePlayground(Playground toUpdate) {
        playgroundDao.save(toUpdate);
    }

    public SportList getAllSports(){

        List<PlaygroundType> playgroundTypes = playgroundTypeDao.readAll();

        SportList sportList = new SportList();

        sportList.setSports(new ArrayList<SportDto>());

        for(PlaygroundType pgType : playgroundTypes){
            SportDto sport = new SportDto(pgType,false);
            sportList.getSports().add(sport);
        }

        return sportList;
    }

    public CoursList getCours(int sportId,boolean includBookins, boolean includMinutesForTime,Date date){
        //get playgrounds
        List<Playground> pgList= playgroundDao.readPlaygroundForSport(sportId);

        if(CollectionUtils.isEmpty(pgList)){
            return null;
        }
        return convertToCoursLists(pgList,date);
    }

    public List<CoursList> getCoursForMerchant(Long merchantId, Integer sportId, boolean includBookings, boolean includMinutesForTime, Date date){
        //check if sportId is null, if null, then set to 0
        sportId = sportId==null?0:sportId;

        List<Playground> playgroundsOfMerchant = playgroundDao.readPlaygroundForMerchant(merchantId,sportId);
        List<CoursList> coursLists = new ArrayList<CoursList>();

        if(sportId>0){
            CoursList coursList =  convertToCoursLists(playgroundsOfMerchant,date);
            coursLists.add(coursList);
        }
        else{
            Map<Integer,List<Playground>> pgGroup =  groupPlaygroundBySportId(playgroundsOfMerchant);


            for(Map.Entry<Integer, List<Playground>> entry : pgGroup.entrySet()){
                CoursList coursList = convertToCoursLists(playgroundsOfMerchant,date);
                coursLists.add(coursList);
            }
        }
        return coursLists;
    }

    private CoursList convertToCoursLists(List<Playground> playgrounds, Date date) {
        if(CollectionUtils.isEmpty(playgrounds)){
            return null;
        }

        CoursList coursList = new CoursList();
        coursList.setCours(new ArrayList<CoursDto>());


        PlaygroundType pgt = playgrounds.get(0).getPlaygroundType();
        coursList.setSport(new SportDto(pgt, false));
        //generatebookinglistForEachplayground
        for (Playground pg : playgrounds) {
            CoursDto coursDto = new CoursDto();

            coursDto.setName(pg.getPlaygroundName());
            coursDto.setCoursId(pg.getId());
            coursDto.setParentId(pg.getParentPlayground() == null ? null : pg.getParentPlayground().getId());

            //set price
            List<PlaygroundOpenTime> pgopts = pg.getOpenTimeList();
            //set properties
            if (pgopts != null && pgopts.size() > 0) {
                PlaygroundOpenTime openTime = calculateOpenTime(pgopts);

                int price = openTime.getPrice() == null ? 0 : openTime.getPrice().getUnitPrice();

                Discount discount = openTime.getPrice().getDiscount();
                BigDecimal finalprice = null;
                if (discount != null) {
                    finalprice = discount.getDiscount().multiply(new BigDecimal(price));
                }
                coursDto.setPrice(finalprice == null ? price : finalprice.intValue());
                coursDto.setUnit(openTime.getPrice().getUnit());
                coursDto.setCoursId(pg.getId());
            }
            //set opentimes

            coursDto.setBookings(getBookingsForPlaygroud(pg.getId(), date, date));

            coursList.getCours().add(coursDto);
        }

        return coursList;
    }

    private  Map<Integer,List<Playground>> groupPlaygroundBySportId(List<Playground> playgroundsOfMerchant) {
        Map<Integer,List<Playground>> playgroundGroupBySportId = new HashMap<Integer,List<Playground>>();

        for(Playground pg : playgroundsOfMerchant){
            Integer key = 0;
            if(pg.getPlaygroundType()!=null){
                key = pg.getPlaygroundType().getId();
            }
            if(playgroundGroupBySportId.get(key)==null){
                playgroundGroupBySportId.put(key,new ArrayList<Playground>());
            }
            playgroundGroupBySportId.get(key).add(pg);
        }

        return playgroundGroupBySportId;
    }

    private List<BookingDto> getBookingsForPlaygroud(Long pgId, Date start, Date end) {
        //today by default
        Calendar startCal = Calendar.getInstance();

        Calendar endCal = Calendar.getInstance();
        if(start!=null){
            startCal.setTime(start);
        }
        startCal.set(Calendar.HOUR_OF_DAY,0);
        startCal.set(Calendar.MINUTE,0);
        startCal.set(Calendar.SECOND,0);

        Calendar cal = Calendar.getInstance();

        if(end!=null){
            cal.setTime(end);
            endCal.set(Calendar.YEAR,cal.get(Calendar.YEAR));
            endCal.set(Calendar.MONTH,cal.get(Calendar.MONTH));
            endCal.set(Calendar.DATE,cal.get(Calendar.DATE));
        }

        endCal.set(Calendar.HOUR_OF_DAY,23);
        endCal.set(Calendar.MINUTE,59);
        endCal.set(Calendar.SECOND,59);



        List<Booking> bookings = bookingDao.getBookingsForPlayground(pgId,startCal.getTime(),endCal.getTime(),
                BookingStatusEnum.LINKED_BOOKING,
                BookingStatusEnum.CREATED,
                BookingStatusEnum.SUBMITTED,
                BookingStatusEnum.WAITING_FOR_PAYMENT);

        ArrayList<BookingDto> bookingDtos = new ArrayList<BookingDto>();

        for(Booking booking : bookings){
            BookingDto bookingDto = new BookingDto();
            bookingDto.setStart(booking.getStartFrom());
            bookingDto.setEnd(booking.getEndAt());
            bookingDtos.add(bookingDto);
        }

        return bookingDtos;
    }

    private ArrayList<String> getOpentimeSlots(PlaygroundOpenTime pgt){

        SimpleDateFormat df =new SimpleDateFormat("HH");

        Date start = pgt.getStartTime();
        Date end = pgt.getEndTime();

        String startTimeStr = df.format(start);
        String endTimeStr = df.format(end);

        int startInt = Integer.valueOf(startTimeStr);
        int endInt = Integer.valueOf(endTimeStr);

        //TODO only support hourly splite, need to add others
        ArrayList<String> openTimeSlot=new ArrayList<String>();
        for(int i = startInt ; i< endInt ; i++){

            String s = String.format("%02d",i);
            String e = String.format("%02d",i+1);

            openTimeSlot.add(s+"-"+e);
        }
        return openTimeSlot;
    }

    private PlaygroundOpenTime calculateOpenTime(List<PlaygroundOpenTime> pgopts){
        PlaygroundOpenTime regularOpenTime=null;

        PlaygroundOpenTime exceptionOpenTime=null;

        PlaygroundOpenTime weekendOpenTime=null;
        //get regimar opentime
        for(PlaygroundOpenTime pgopt : pgopts){
            if(pgopt.isDailyOpenTime()){
                    regularOpenTime = pgopt;
            }else if(pgopt.isExceptionalOpenTime()){
                if(pgopt.getExceptionalDate()!=null){
                    Date exceptionalDate = pgopt.getExceptionalDate();
                    if(exceptionalDate.compareTo(new Date())==0){
                        exceptionOpenTime = pgopt;
                        break;
                    }
                }
            }else if(pgopt.isWeekendOpenTime()){
                if(isWeekend()){
                    //today is weekend
                    weekendOpenTime = pgopt;
                    break;
                }
            }
        }
        //get exception opentime
        if(exceptionOpenTime!=null){
            return exceptionOpenTime;
        }
        if(isWeekend()&&weekendOpenTime!=null){
            return weekendOpenTime;
        }
        return regularOpenTime;


    }

    private boolean isWeekend(){
        Calendar today = Calendar.getInstance();
        int dayofWeek = today.get(Calendar.DAY_OF_WEEK);
        if(Calendar.SATURDAY == dayofWeek || Calendar.SUNDAY==dayofWeek) {
            //today is weekend
            return true;
        }else{
            return false;
        }
    }

    public List<BookingDto> readBookingOfPlayground(Long playgroundId) {
        return this.getBookingsForPlaygroud(playgroundId, new Date(),new Date());
    }


}
